package com.yoke.assistant.service;

import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.content.Context;
import android.graphics.Rect;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
import android.util.Log;
import android.view.KeyEvent;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.webkit.WebView;
import android.webkit.WebViewClient;

import com.yoke.assistant.bridge.JsApp;
import com.yoke.assistant.bridge.JsLogger;
import com.yoke.assistant.event.JsEvent;
import com.yoke.assistant.remote.LocalUDPDataReciever;
import com.yoke.assistant.utils.AppUtil;
import com.yoke.assistant.utils.ListUtil;

import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class AssistantService extends AccessibilityService {

    public static final String TAG = "mService";
    public static AssistantService service;
    private WebView mWebView = null;

    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {

        Log.d(TAG, "onAccessibilityEvent:"
                + " EventType:"+ event.getEventType()
                + " EventTime:"+ event.getEventTime()
                + " PackageName:"+ event.getPackageName()
                + " ClassName:"+ event.getClassName()
                + " Text:"+ event.getText()
        );
    }

    @Override
    protected boolean onKeyEvent(KeyEvent event) {
        Log.i(TAG, "按钮点击变化");
        //接收按键事件
        return super.onKeyEvent(event);
    }

    @Override
    public void onInterrupt() {
        Log.i(TAG, "授权中断");
        //服务中断，如授权关闭或者将服务杀死
    }

    @Override
    protected void onServiceConnected() {
        super.onServiceConnected();
        Log.i(TAG, "service授权成功");
        service = this;
        if (mWebView == null) {
            initWebView();
        }
        EventBus.getDefault().register(this);
        LocalUDPDataReciever.getInstance(AppUtil.sContext).startup();

    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        cleanWebView();
        EventBus.getDefault().unregister(this);
        LocalUDPDataReciever.getInstance(AppUtil.sContext).stop();
    }


    /**
     * 判断当前服务是否正在运行
     */
    public static boolean isRunning() {
        if (service == null) {
            return false;
        }
        AccessibilityManager accessibilityManager = (AccessibilityManager) service.getSystemService(
                Context.ACCESSIBILITY_SERVICE);
        AccessibilityServiceInfo info = service.getServiceInfo();
        if (info == null) {
            return false;
        }
        List<AccessibilityServiceInfo> list =
                accessibilityManager.getEnabledAccessibilityServiceList(
                        AccessibilityServiceInfo.FEEDBACK_GENERIC);
        Iterator<AccessibilityServiceInfo> iterator = list.iterator();

        boolean isConnect = false;
        while (iterator.hasNext()) {
            AccessibilityServiceInfo i = iterator.next();
            if (i.getId().equals(info.getId())) {
                isConnect = true;
                break;
            }
        }
        if (!isConnect) {
            return false;
        }
        return true;
    }


    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onEvent(JsEvent event) {
        loadJs(event.getJsStr());
    }

    /**
     * 初始化webview
     */
    protected void initWebView() {
        mWebView = new WebView(this);
        mWebView.getSettings().setDefaultTextEncodingName("utf-8");
        mWebView.getSettings().setJavaScriptEnabled(true);
        mWebView.addJavascriptInterface(new JsLogger(), "logger");//JsToJava是内部类，代码在后面。stub是接口名字。
        mWebView.addJavascriptInterface(new JsApp(), "app");//JsToJava是内部类，代码在后面。stub是接口名字。
//        mWebView.loadUrl("file:///android_asset/jsHelper.html"); //js文件路径
        mWebView.setWebViewClient(new WebViewClient() {
            @Override
            public void onPageFinished(WebView view, String url) {

            }
        });
    }

    public void loadJs(String jsStr) {
        mWebView.loadUrl("javascript:" + jsStr);
    }

    void cleanWebView() {
        // 清WebView
        // 清WebView
        if (mWebView != null) {
            mWebView.setWebChromeClient(null);
            mWebView.setWebViewClient(null);
            mWebView.getSettings().setJavaScriptEnabled(false);
            mWebView.loadDataWithBaseURL(null, "", "text/html", "utf-8", null);
            mWebView.clearHistory();
            mWebView.clearCache(true);
            mWebView.freeMemory();
            mWebView.removeAllViews();
            mWebView.destroy();
            mWebView = null;
        }
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 公共方法
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * 点击该控件
     *
     * @return true表示点击成功
     */
    public boolean clickView(AccessibilityNodeInfo nodeInfo) {
        if (nodeInfo != null) {
            if (nodeInfo.isClickable()) {
                nodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK);
                return true;
            } else {
                AccessibilityNodeInfo parent = nodeInfo.getParent();
                if (parent != null) {
                    boolean b = clickView(parent);
                    parent.recycle();
                    if (b) return true;
                }
            }
        }
        return false;
    }

    /**
     * 根据getRootInActiveWindow查找包含当前text的控件
     *
     * @param containsText 只要内容包含就会找到（应该是根据drawText找的）
     */
    @Nullable
    public List<AccessibilityNodeInfo> findViewByContainsText(@NonNull String containsText) {
        AccessibilityNodeInfo info = getRootInActiveWindow();
        if (info == null) return null;
        List<AccessibilityNodeInfo> list = info.findAccessibilityNodeInfosByText(containsText);
        info.recycle();
        return list;
    }

    /**
     * 根据getRootInActiveWindow查找和当前text相等的控件
     *
     * @param equalsText 需要找的text
     */
    @Nullable
    public List<AccessibilityNodeInfo> findViewByEqualsText(@NonNull String equalsText) {
        List<AccessibilityNodeInfo> listOld = findViewByContainsText(equalsText);
        if (ListUtil.isEmpty(listOld)) {
            return null;
        }
        ArrayList<AccessibilityNodeInfo> listNew = new ArrayList<>();
        for (AccessibilityNodeInfo ani : listOld) {
            if (ani.getText() != null && equalsText.equals(ani.getText().toString())) {
                listNew.add(ani);
            } else {
                ani.recycle();
            }
        }
        return listNew;
    }

    /**
     * 根据getRootInActiveWindow查找当前id的控件
     *
     * @param pageName 被查找项目的包名:com.android.xxx
     * @param idName   id值:tv_main
     */
    @Nullable
    public AccessibilityNodeInfo findViewById(String pageName, String idName) {
        if(pageName == null){
            pageName = getRootInActiveWindow().getPackageName().toString();
        }
        return findViewById(pageName + ":id/" + idName);
    }

    /**
     * 根据getRootInActiveWindow查找当前id的控件
     *
     * @param idfullName id全称:com.android.xxx:id/tv_main
     */
    @Nullable
    public AccessibilityNodeInfo findViewById(String idfullName) {
        List<AccessibilityNodeInfo> list = findViewByIdList(idfullName);
        return ListUtil.first(list);
    }

    /**
     * 根据getRootInActiveWindow查找当前id的控件集合(类似listview这种一个页面重复的id很多)
     *
     * @param idfullName id全称:com.android.xxx:id/tv_main
     */
    @Nullable
    public List<AccessibilityNodeInfo> findViewByIdList(String idfullName) {
        try {
            AccessibilityNodeInfo rootInfo = getRootInActiveWindow();
            if (rootInfo == null) return null;
            List<AccessibilityNodeInfo> list = null;
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
                list = rootInfo.findAccessibilityNodeInfosByViewId(idfullName);
            }
            rootInfo.recycle();
            return list;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 只找第一个ClassName
     * 此方法效率相对较低,建议使用之后保存id然后根据id进行查找
     */
    public AccessibilityNodeInfo findViewByFirstClassName(String className) {
        AccessibilityNodeInfo rootInfo = getRootInActiveWindow();
        if (rootInfo == null) return null;
        AccessibilityNodeInfo info = findViewByFirstClassName(rootInfo, className);
        rootInfo.recycle();
        return info;
    }

    /**
     * 只找第一个ClassName
     * 此方法效率相对较低,建议使用之后保存id然后根据id进行查找
     */
    public static AccessibilityNodeInfo findViewByFirstClassName(AccessibilityNodeInfo parent,
            String className) {
        if (parent == null) return null;
        for (int i = 0; i < parent.getChildCount(); i++) {
            AccessibilityNodeInfo child = parent.getChild(i);
            if (child == null) continue;
            if (className.equals(child.getClassName().toString())) {
                return child;
            }
            AccessibilityNodeInfo childChild = findViewByFirstClassName(child, className);
            child.recycle();
            if (childChild != null) {
                return childChild;
            }
        }
        return null;
    }

    /**
     * 此方法效率相对较低,建议使用之后保存id然后根据id进行查找
     */
    public List<AccessibilityNodeInfo> findViewByClassName(String className) {
        ArrayList<AccessibilityNodeInfo> list = new ArrayList<>();
        AccessibilityNodeInfo rootInfo = getRootInActiveWindow();
        if (rootInfo == null) return list;
        findViewByClassName(list, rootInfo, className);
        rootInfo.recycle();
        return list;
    }

    /**
     * 此方法效率相对较低,建议使用之后保存id然后根据id进行查找
     */
    public static void findViewByClassName(List<AccessibilityNodeInfo> list,
            AccessibilityNodeInfo parent, String className) {
        if (parent == null) return;
        for (int i = 0; i < parent.getChildCount(); i++) {
            AccessibilityNodeInfo child = parent.getChild(i);
            if (child == null) continue;
            if (className.equals(child.getClassName().toString())) {
                list.add(child);
            } else {
                findViewByClassName(list, child, className);
                child.recycle();
            }
        }
    }

    /**
     * 只找第一个相等的ContentDescription
     * 此方法效率相对较低,建议使用之后保存id然后根据id进行查找
     */
    public AccessibilityNodeInfo findViewByFirstEqualsContentDescription(
            String contentDescription) {
        AccessibilityNodeInfo rootInfo = getRootInActiveWindow();
        if (rootInfo == null) return null;
        AccessibilityNodeInfo info = findViewByFirstEqualsContentDescription(rootInfo,
                contentDescription);
        rootInfo.recycle();
        return info;
    }

    /**
     * 只找第一个相等的ContentDescription
     * 此方法效率相对较低,建议使用之后保存id然后根据id进行查找
     */
    public static AccessibilityNodeInfo findViewByFirstEqualsContentDescription(
            AccessibilityNodeInfo parent, String contentDescription) {
        if (parent == null) return null;
        for (int i = 0; i < parent.getChildCount(); i++) {
            AccessibilityNodeInfo child = parent.getChild(i);
            if (child == null) continue;
            CharSequence cd = child.getContentDescription();
            if (cd != null && contentDescription.equals(cd.toString())) {
                return child;
            }
            AccessibilityNodeInfo childChild = findViewByFirstEqualsContentDescription(child,
                    contentDescription);
            child.recycle();
            if (childChild != null) {
                return childChild;
            }
        }
        return null;
    }

    /**
     * 只找第一个包含的ContentDescription
     * 此方法效率相对较低,建议使用之后保存id然后根据id进行查找
     */
    public AccessibilityNodeInfo findViewByFirstContainsContentDescription(
            String contentDescription) {
        AccessibilityNodeInfo rootInfo = getRootInActiveWindow();
        if (rootInfo == null) return null;
        AccessibilityNodeInfo info = findViewByFirstContainsContentDescription(rootInfo,
                contentDescription);
        rootInfo.recycle();
        return info;
    }

    /**
     * 只找第一个包含的ContentDescription
     * 此方法效率相对较低,建议使用之后保存id然后根据id进行查找
     */
    public static AccessibilityNodeInfo findViewByFirstContainsContentDescription(
            AccessibilityNodeInfo parent, String contentDescription) {
        if (parent == null) return null;
        for (int i = 0; i < parent.getChildCount(); i++) {
            AccessibilityNodeInfo child = parent.getChild(i);
            if (child == null) continue;
            CharSequence cd = child.getContentDescription();
            if (cd != null && cd.toString().contains(contentDescription)) {
                return child;
            }
            AccessibilityNodeInfo childChild = findViewByFirstContainsContentDescription(child,
                    contentDescription);
            child.recycle();
            if (childChild != null) {
                return childChild;
            }
        }
        return null;
    }

    /**
     * 此方法效率相对较低,建议使用之后保存id然后根据id进行查找
     */
    public List<AccessibilityNodeInfo> findViewByContentDescription(String contentDescription) {
        ArrayList<AccessibilityNodeInfo> list = new ArrayList<>();
        AccessibilityNodeInfo rootInfo = getRootInActiveWindow();
        if (rootInfo == null) return list;
        findViewByContentDescription(list, rootInfo, contentDescription);
        rootInfo.recycle();
        return list;
    }

    /**
     * 此方法效率相对较低,建议使用之后保存id然后根据id进行查找
     */
    public static void findViewByContentDescription(List<AccessibilityNodeInfo> list,
            AccessibilityNodeInfo parent, String contentDescription) {
        if (parent == null) return;
        for (int i = 0; i < parent.getChildCount(); i++) {
            AccessibilityNodeInfo child = parent.getChild(i);
            if (child == null) continue;
            CharSequence cd = child.getContentDescription();
            if (cd != null && contentDescription.equals(cd.toString())) {
                list.add(child);
            } else {
                findViewByContentDescription(list, child, contentDescription);
                child.recycle();
            }
        }
    }

    /**
     * 此方法效率相对较低,建议使用之后保存id然后根据id进行查找
     */
    public List<AccessibilityNodeInfo> findViewByRect(Rect rect) {
        ArrayList<AccessibilityNodeInfo> list = new ArrayList<>();
        AccessibilityNodeInfo rootInfo = getRootInActiveWindow();
        if (rootInfo == null) return list;
        findViewByRect(list, rootInfo, rect);
        rootInfo.recycle();
        return list;
    }

    public static Rect mRecycleRect = new Rect();

    /**
     * 此方法效率相对较低,建议使用之后保存id然后根据id进行查找
     */
    public static void findViewByRect(List<AccessibilityNodeInfo> list,
            AccessibilityNodeInfo parent, Rect rect) {
        if (parent == null) return;
        for (int i = 0; i < parent.getChildCount(); i++) {
            AccessibilityNodeInfo child = parent.getChild(i);
            if (child == null) continue;
            child.getBoundsInScreen(mRecycleRect);
            if (mRecycleRect.contains(rect)) {
                list.add(child);
            } else {
                findViewByRect(list, child, rect);
                child.recycle();
            }
        }
    }

    /**
     * 由于太多,最好回收这些AccessibilityNodeInfo
     */
    public static void recycleAccessibilityNodeInfo(List<AccessibilityNodeInfo> listInfo) {
        if (ListUtil.isEmpty(listInfo)) return;

        for (AccessibilityNodeInfo info : listInfo) {
            info.recycle();
        }
    }

}
